#include "easyShell.h"

int easyShellCmdVariableRefresh(int argc, char** argv);

static shellCmdTypedef shellCmdList[ES_CMD_MAX]= {0};
static uint8_t shellCmdCount=0;

uint8_t cmdBuf[ES_CMD_LENGTH_MAX+1]= {0};
static uint16_t cmdLen=0;
static uint16_t cmdCurPos=0;

static uint8_t cmdTempBuf[5]= {0};
static uint8_t cmdTempCount=0;
static bool isMultipleCmdTemp=false;

static bool shellTimeOutEn=false;
static uint8_t shellTimeOutCount=0;

static uint32_t easyShellTickValue=0;
static cmdFunc_t loopFun=NULL;
#if ES_TAB_ENABLE == 1
static uint8_t tabCount=0;
#endif


#if ES_VARIABLE_ENABLE == 1
typedef struct {
    uint8_t showName[ES_VARIABLE_NAME_MAX+1];
    uint8_t varType;
    void* var;
    uint8_t tableCellWidth;
    uint8_t decHex;
} variableTypedef;

static variableTypedef shellVar[ES_VARIABLE_MAX];
#endif


static void (*shellSend)(char*,uint16_t);

bool easyShellAddCmd(shellCmdTypedef info)
{
    if(shellCmdCount<ES_CMD_MAX)
    {
        shellCmdList[shellCmdCount]=info;
        shellCmdCount++;
        return true;
    }
    return false;
}

void easyShellShowLogo(void)
{
    char buf[]="\n\r\n\r\
                       ____  _          _ _ \n\r\
   ___  __ _ ___ _   _/ ___|| |__   ___| | |\n\r\
  / _ \\/ _` / __| | | \\___ \\| '_ \\ / _ \\ | |\n\r\
 |  __/ (_| \\__ \\ |_| |___) | | | |  __/ | |\n\r\
  \\___|\\__,_|___/\\__, |____/|_| |_|\\___|_|_|\n\r\
                 |___/                      \n\r\n\r\
  https://gitee.com/gzbkey/easyShell.git\n\r\n\r";

    shellSend(buf,sizeof(buf)-1);
}

void easyShellShowCursor(void)
{
    shellSend(">>",2);
}


void easyShellInit(void (*pUartSend)(char*,uint16_t))
{
    shellSend=pUartSend;
    easyShellShowLogo();
    easyShellShowCursor();
    easyShellTickValue=0;
#if ES_VARIABLE_ENABLE == 1
    shellCmdTypedef varCmdInfo={
        .pName="var",
    #if ES_CMD_BRIEF_ENABLE == 1
        .pBrief="variable view",
    #endif
        .pFunc=easyShellCmdVariableRefresh,
    };
    easyShellAddCmd(varCmdInfo);
    for(uint8_t i=0; i<ES_VARIABLE_MAX; i++)
    {
        shellVar[i].showName[0]=0;
    }
#endif
}

void easyShellLoop(void)
{
    if(easyShellTickValue>=1000)
    {
        easyShellTickValue=0;
        if(loopFun!=NULL)
        {
            loopFun(0,NULL);
        }
    }
}

void easyShellCmdReset(void)
{
    cmdLen=0;
    cmdCurPos=0;
    memset(cmdBuf,0,sizeof(cmdBuf));
}

void easyShellTimeOutReset(void)
{
    shellTimeOutEn=false;
    shellTimeOutCount=0;

    memset(cmdTempBuf,0,sizeof(cmdTempBuf));
    cmdTempCount=0;
    isMultipleCmdTemp=false;
}

void easyShellTick(uint16_t cycleMs)
{
    uint8_t temp;

    //loop使用计时
    easyShellTickValue+=cycleMs;

    if(shellTimeOutEn==true)
    {
        temp=150/cycleMs;
        temp=(temp<=1)?2:temp;

        shellTimeOutCount++;
        if(shellTimeOutCount>=temp)
        {
            easyShellTimeOutReset();
        }
    }
}

uint8_t easyShellCmdProcess(uint8_t *str,uint8_t** ret)
{
    uint8_t i;
    uint8_t *addr=NULL;
    uint8_t retCount=0;

    addr=str;
    ret[0]=addr;

    retCount++;

    for(i=0; i<ES_CMD_PARA_MAX; i++)
    {
        addr=(uint8_t *)strstr((const char*)addr," ");
        if(addr!=0)
        {
            *addr=0;
            addr++;
            ret[i+1]=addr;
            retCount++;
        }
        else
        {
            break;
        }
    }
    return retCount;
}

void easyShellRunCmd(uint8_t *dat,uint16_t len)
{
    int i=0;
    uint8_t* argv[ES_CMD_PARA_MAX+1]= {0};
    uint8_t count;

    count=easyShellCmdProcess(dat,argv);
    for(i=0; i<shellCmdCount; i++)
    {
        if(strcmp(shellCmdList[i].pName,(const char*)argv[0])==0)
        {
            shellSend("\r\n",2);
            shellCmdList[i].pFunc(count,(char **)argv);
            break;
        }
#if ES_CMD_BRIEF_ENABLE == 1
        if((strcmp("man",(const char*)argv[0])==0)&&(strcmp(shellCmdList[i].pName,(const char*)argv[1])==0))
        {
            shellSend("\r\n",2);
            shellSend(shellCmdList[i].pBrief,strlen(shellCmdList[i].pBrief));
            break;
        }
#endif
    }
    shellSend("\r\n",2);
    if((i==shellCmdCount)&&(len>0))//无匹配
    {
        shellSend("Command not found\r\n",19);
    }
    easyShellShowCursor();
    easyShellTimeOutReset();
    easyShellCmdReset();
}

#if ES_TAB_ENABLE == 1

uint8_t easyShellListMatchCmd(uint8_t *nameStr,uint8_t *outNumList)
{
    uint8_t i;
    uint8_t *addr=NULL;
    uint8_t cmdMatchCount=0;

    for(i=0; i<shellCmdCount; i++)
    {
        addr=(uint8_t*)strstr(shellCmdList[i].pName,(const char*)nameStr);
        if(addr!=0)
        {
            outNumList[cmdMatchCount]=i;
            cmdMatchCount++;
        }
    }
    return cmdMatchCount;
}

//自动换行
void easyShellListCmdAutoPrint(uint8_t num,char *dat,uint16_t length)
{
    static uint16_t count;
    if(num==0)
    {
        count=0;
    }

    if(length<=80)
    {
        if(count+length<80)
        {
            shellSend(dat,length);
        }
        else
        {
            count=0;
            shellSend("\r\n",2);
            shellSend(dat,length);
        }
        count=count+length+1;
        shellSend(" ",1);
    }
}

void easyShellListCmd(void)
{
    uint8_t *addr=NULL,*tempAddr=NULL;
    uint8_t cmdMatchNum[ES_CMD_MAX]= {0};
    uint8_t cmdMatchCount=0;
    int16_t i;
    if(cmdLen==cmdCurPos)
    {
        //反向查找空格
        for(i=cmdLen; i>0; i--)
        {
            if(cmdBuf[i-1]==' ')
            {
                addr=&cmdBuf[i-1];
                addr++;
            }
        }

        if(addr==NULL)
        {
            addr=cmdBuf;
        }

        //匹配命令
        if(cmdLen!=0)
        {
            cmdMatchCount=easyShellListMatchCmd(addr,cmdMatchNum);
        }

        switch (cmdMatchCount)
        {
        case 0:
        {
            if(cmdBuf[0]==0)
            {
                shellSend("\r\n",2);
                for(i=0; i<shellCmdCount; i++)
                {
                    //列出
                    easyShellListCmdAutoPrint(i,shellCmdList[i].pName,strlen(shellCmdList[i].pName));
                }
                shellSend("\r\n",2);
                easyShellShowCursor();
                shellSend(cmdBuf,cmdLen);
            }
            break;
        }
        case 1:
        {
            tempAddr=shellCmdList[cmdMatchNum[0]].pName;
            while(*tempAddr!=0)
            {
                if(*tempAddr!=*addr)
                {
                    cmdBuf[cmdLen]=*tempAddr;
                    cmdLen++;
                    cmdCurPos++;
                    shellSend(tempAddr,1);

                }
                tempAddr++;
                addr++;
            }
            break;
        }
        default:
        {
            if(tabCount==0)
            {
                tabCount++;
                uint8_t newStrLen=0;
                uint8_t strPos=0;
                newStrLen=strlen((const char*)addr);
                strPos=newStrLen;
                //判断下一次字节是否相同
                tempAddr=shellCmdList[cmdMatchNum[0]].pName;
                while(tempAddr[strPos]!=0)
                {
                    for(i=1; i<cmdMatchCount; i++)
                    {
                        if(tempAddr[strPos]!=shellCmdList[cmdMatchNum[i]].pName[strPos])
                        {
                            break;
                        }
                    }
                    if(i==cmdMatchCount)
                    {
                        cmdBuf[strPos]=tempAddr[strPos];
                        shellSend(&cmdBuf[strPos],1);
                        cmdLen++;
                        cmdCurPos++;
                    }
                    strPos++;
                }
            }
            else
            {
                tabCount=0;
                shellSend("\r\n",2);
                for(i=0; i<cmdMatchCount; i++)
                {
                    //列出
                    easyShellListCmdAutoPrint(i,shellCmdList[cmdMatchNum[i]].pName,strlen(shellCmdList[cmdMatchNum[i]].pName));
                }
                shellSend("\r\n",2);
                easyShellShowCursor();
                shellSend(cmdBuf,cmdLen);
            }
            break;
        }
        }
    }
}
#endif

void easyShellReceiveData(uint8_t dat)
{
    int32_t i;

    cmdTempBuf[cmdTempCount]=dat;

    if(isMultipleCmdTemp)
    {
        //左箭头  1B 5B 44
        //右箭头  1B 5B 43
        //Delete  1B 5B 33 7E
        cmdTempCount++;
        shellTimeOutEn=true;
#if ES_CURSOR_MOVE_ENABLE == 1
        if(cmdTempCount==3)
        {
            //左箭头  1B 5B 44
            if((cmdTempBuf[0]==0x1B)&&(cmdTempBuf[1]==0x5B)&&(cmdTempBuf[2]==0x44))
            {
                if(cmdCurPos>0)
                {
                    shellSend("\b",1);
                    cmdCurPos--;
                    easyShellTimeOutReset();
                }
            }

            //右箭头  1B 5B 43
            if((cmdTempBuf[0]==0x1B)&&(cmdTempBuf[1]==0x5B)&&(cmdTempBuf[2]==0x43))
            {
                if(cmdCurPos<cmdLen)
                {
                    shellSend(&cmdBuf[cmdCurPos],1);
                    cmdCurPos++;
                    easyShellTimeOutReset();
                }
            }
        }
        else
        {
            if(cmdTempCount==4)
            {
                //Delete  1B 5B 33 7E
                if((cmdTempBuf[0]==0x1B)&&(cmdTempBuf[1]==0x5B)&&(cmdTempBuf[2]==0x33)&&(cmdTempBuf[3]==0x7E))
                {
                    if(cmdCurPos<cmdLen)
                    {
                        for(i=cmdCurPos; i<(cmdLen-1); i++)
                        {
                            cmdBuf[i]=cmdBuf[i+1];
                        }

                        cmdBuf[cmdLen-1]=' ';

                        shellSend(&cmdBuf[cmdCurPos],cmdLen-cmdCurPos+1);

                        for(i=cmdCurPos; i<(cmdLen); i++)
                        {
                            shellSend("\b",1);
                        }

                        cmdLen--;
                        easyShellTimeOutReset();
                    }
                }
            }
        }
#endif
    }
    else
    {
        switch(dat)
        {
        case 0x03://ETX (end of text)
        {
            loopFun=NULL;
            shellSend("\r\n",2);
            easyShellShowCursor();
            break;
        }
        //rn  r  n
        case '\n'://换行
        {
            if(cmdTempCount==0)
            {
                easyShellRunCmd(cmdBuf,cmdLen);
            }
            easyShellTimeOutReset();
            break;
        }
        case '\r'://回车
        {
            easyShellRunCmd(cmdBuf,cmdLen);
            cmdTempCount++;
            shellTimeOutEn=true;
            break;
        }
        case '\b'://Backspace
        {
            if(cmdLen>0)
            {
                tabCount=0;
                if(cmdCurPos==cmdLen)
                {
                    shellSend("\b \b",3);
                    cmdBuf[cmdLen-1]=0;
                    cmdLen--;
                    cmdCurPos--;
                    
                }
                else//中间删除
                {
                    for(i=cmdCurPos; i<cmdLen; i++)
                    {
                        cmdBuf[i-1]=cmdBuf[i];
                    }
                    cmdLen--;
                    cmdCurPos--;

                    cmdBuf[cmdLen]=' ';

                    shellSend("\b",1);
                    shellSend(&cmdBuf[cmdCurPos],cmdLen-cmdCurPos+1);
                    for(i=0; i<cmdLen-cmdCurPos+1; i++)
                    {
                        shellSend("\b",1);
                    }
                }
            }
            break;
        }

        //左箭头  1B 5B 44
        //右箭头  1B 5B 43
        //Delete  1B 5B 33 7E
        case 0x1B:
        {
            isMultipleCmdTemp=true;
            cmdTempCount++;
            shellTimeOutEn=true;
            break;
        }

        case '\t'://tab
        {
#if ES_TAB_ENABLE == 1
            easyShellListCmd();
#endif
            break;
        }
        default:
        {
            if(cmdLen<ES_CMD_LENGTH_MAX)
            {
                tabCount=0;
                if(cmdCurPos==cmdLen)
                {
                    cmdBuf[cmdLen++]=dat;
                    cmdCurPos++;
                    shellSend(&dat,1);
                }
                else//插入
                {
                    for(i=cmdLen; i>cmdCurPos; i--)
                    {
                        cmdBuf[i]=cmdBuf[i-1];
                    }
                    cmdLen++;

                    cmdBuf[cmdCurPos]=dat;
                    shellSend(&cmdBuf[cmdCurPos],cmdLen-cmdCurPos);
                    cmdCurPos++;
                    for(i=0; i<cmdLen-cmdCurPos; i++)
                    {
                        shellSend("\b",1);
                    }
                }
            }
        }
        }
    }
}

#if ES_VARIABLE_ENABLE == 1
void easyShellPrintVariable(uint8_t varType,void* var,uint8_t width,uint8_t decHex)
{
    int32_t *i32Temp;
    float *dTemp;
    uint8_t tempBuf[64]= {0};
    uint8_t sprintfCmd[8]="%16d";
    switch(varType)
    {
    case VAR_TYPE_INT_32:
    {
        i32Temp=var;
        if(decHex==10)
        {
            sprintf((char*)sprintfCmd,"%%-%dd",width);
        }
        else
        {
            sprintf((char*)sprintfCmd,"0x%%-%dX",width-2);
        }
        sprintf((char*)tempBuf,(const char*)sprintfCmd,*i32Temp);
        break;
    }
    case VAR_TYPE_FLOAT:
    {
        dTemp=var;
        sprintf((char*)sprintfCmd,"%%-%df",width);
        sprintf((char*)tempBuf,(const char*)sprintfCmd,*dTemp);
        break;
    }
    default:
        break;
    }
    shellSend(tempBuf,strlen((const char*)tempBuf));
}

#define FLOAT_WIDTH       12
#define INT32_DEC_WIDTH   10
#define INT32_HEX_WIDTH   6

void easyShellCmdVariableAdd(uint8_t num,uint8_t* showName,uint8_t varType,void* var,uint8_t decHex)
{
    uint8_t nameLen;
    if(num<ES_VARIABLE_MAX)
    {
        nameLen=strlen((const char*)showName);
        nameLen=(ES_VARIABLE_NAME_MAX<nameLen)?ES_VARIABLE_NAME_MAX:nameLen;

        memcpy(shellVar[num].showName,showName,nameLen);
        shellVar[num].varType=varType;
        shellVar[num].var=var;
        if(varType==VAR_TYPE_INT_32)
        {
            if(decHex==10)
            {
                shellVar[num].decHex=10;
                shellVar[num].tableCellWidth=INT32_DEC_WIDTH;
            }
            else
            {
                shellVar[num].decHex=16;
                shellVar[num].tableCellWidth=INT32_HEX_WIDTH;
            }
        }
        else
        {
            shellVar[num].decHex=10;
            shellVar[num].tableCellWidth=FLOAT_WIDTH;
        }
        
    }
}

#define MAX(a,b)  (((a)>(b))?(a):(b))
int easyShellCmdVariableRefresh(int argc, char** argv)
{
    uint8_t i,j;
    uint8_t tableCellWidth;
    uint8_t buf=0x5F;
    
    if(argc!=0)//首次
    {
        loopFun=easyShellCmdVariableRefresh;
        
        for(i=0; i<ES_VARIABLE_MAX; i++)
        {
            if(shellVar[i].showName[0]!=0)
            {
                tableCellWidth=MAX(ES_VARIABLE_NAME_MAX,shellVar[i].tableCellWidth);
                for(j=0; j<tableCellWidth; j++)
                {
                    shellSend(&buf,1);
                }
            }
        }
        shellSend("\r\n",2);
        
        for(i=0; i<ES_VARIABLE_MAX; i++)
        {
            if(shellVar[i].showName[0]!=0)
            {
                tableCellWidth=MAX(ES_VARIABLE_NAME_MAX,shellVar[i].tableCellWidth);
                shellSend(shellVar[i].showName,strlen((const char*)shellVar[i].showName));
                for(j=strlen((const char*)shellVar[i].showName); j<tableCellWidth; j++)
                {
                    shellSend(" ",1);
                }
            }
        }
        shellSend("\r\n",2);
    }
    shellSend("\r",1);

    for(i=0; i<ES_VARIABLE_MAX; i++)
    {
        if(shellVar[i].showName[0]!=0)
        {
            tableCellWidth=MAX(ES_VARIABLE_NAME_MAX,shellVar[i].tableCellWidth);
            easyShellPrintVariable(shellVar[i].varType,shellVar[i].var,tableCellWidth,shellVar[i].decHex);
        }
    }
    return -1;
}
#endif


